package cn.anecansaitin.firecrafting.common.crafting;

import cn.anecansaitin.firecrafting.ModConstant;
import cn.anecansaitin.firecrafting.api.common.IResultType;
import cn.anecansaitin.firecrafting.api.common.block.entity.IFireInterfaceEntity;
import cn.anecansaitin.firecrafting.api.client.crafting.IJEIRecipeRender;
import cn.anecansaitin.firecrafting.client.crafting.FireItemRecipeRender;
import cn.anecansaitin.firecrafting.common.Config;
import cn.anecansaitin.firecrafting.common.util.Directions;
import cn.anecansaitin.firecrafting.common.util.DropHelper;
import cn.anecansaitin.firecrafting.common.util.ModItemHandlerHelper;
import cn.anecansaitin.firecrafting.common.util.ModJsonReader;
import com.google.common.collect.ImmutableList;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.NonNullList;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.ForgeRegistryEntry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;

//IFireRecipe的实现类，执行具体的配方检测、产物生成等行为
public class FireItemRecipe extends SimpleFireRecipe {
    private final ImmutableList<ItemStack> outputs;

    public FireItemRecipe(ResourceLocation id, ImmutableList<Block> fireBlock, ImmutableList<ItemStack> outputs, NonNullList<Ingredient> inputs) {
        super(id, fireBlock, inputs);
        this.outputs = outputs;
    }

    @Override
    public IJEIRecipeRender getRender() {
        return FireItemRecipeRender.INSTANCE;
    }

    @Override
    public @NotNull NonNullList<Ingredient> getIngredients() {
        return super.getIngredients();
    }

    //在配方正确时进行一系列行为
    @Override
    public boolean doAction(List<ItemStack> stacks, BlockPos pos, Level world) {
        //进行配方检测
        if (match(stacks, pos, world)) {
            //用于记录剩余物品
            ArrayList<ItemStack> remain = new ArrayList<>();
            //复制
            outputs.forEach(stack -> remain.add(stack.copy()));
            //为火焰六个面的方块进行检测，每个面都尝试存入物品
            for (Direction direction : Directions.ALL_SIDE) {
                //检测物品剩余
                if (remain.isEmpty()) {
                    return true;
                }
                //获取指定方向的方块实体
                BlockEntity entity = world.getBlockEntity(pos.relative(direction));
                //判断方块实体存在
                if (entity != null) {
                    //获取指定方向的物品能力（获取朝向火焰的那个面）
                    LazyOptional<IItemHandler> cap = entity.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, direction.getOpposite());
                    //判断能力存在
                    if (cap.isPresent()) {
                        //获取能力对象
                        IItemHandler item = cap.orElseThrow(RuntimeException::new);
                        //存入物品
                        for (int i = 0, remainSize = remain.size(); i < remainSize; i++) {
                            ItemStack stack = ItemHandlerHelper.insertItem(item, remain.get(i), false);
                            remain.set(i, stack);
                        }

                        //移除空的物品
                        remain.removeIf(ItemStack::isEmpty);
                    }
                }
            }
            if (remain.isEmpty()) {
                return true;
            }
            //在世界上生成所有产物
            remain.forEach((stack -> DropHelper.dropItemStack(world, pos.above(), stack, false, (item) -> {
                //设置实体不受重力
                item.setNoGravity(true);
                //设置实体一段时间的抗火
                item.setRemainingFireTicks(-Config.WORLD_MAX_FIRE_PROTECTION.get());
                //为实体添加tag，用于注明为合成产物，避免被火烧毁后计入配方中
                item.addTag(TYPE_ID);
                //设置移动速度为0
                item.setDeltaMovement(0, 0, 0);
            })));
            return true;
        }

        return false;
    }

    @Override
    public int doAction(IFireInterfaceEntity fireInterface) {
        //获取该接口能处理的类型
        List<IResultType> types = fireInterface.actionType();
        //判断接口支持的类型
        if (types.stream().anyMatch(IResultType.ITEM::equals)) {
            //检测配方
            if (!match(fireInterface.getMaterial(), fireInterface.getFire())) {
                return -1;
            }
            //获取能量能力
            LazyOptional<IItemHandler> energyHandler = fireInterface.getCapForOutput(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY);
            //如果不能力存在
            if (!energyHandler.isPresent()) {
                return -1;
            }
            //获取物品对象
            IItemHandler item = energyHandler.orElseThrow(() -> new RuntimeException("不可能触发的异常"));
            //尝试存入
            if (!ModItemHandlerHelper.insertItem(item, outputs, true)) {
                return 0;
            }
            //复制
            ArrayList<ItemStack> stacks = new ArrayList<>();
            outputs.forEach(stack -> stacks.add(stack.copy()));
            //正真存入
            ModItemHandlerHelper.insertItem(item, stacks, false);
            return 1;
        }

        return -1;
    }

    public List<ItemStack> getOutputs() {
        return outputs;
    }

    //返回配方指定的序列化器
    @Override
    public @NotNull RecipeSerializer<?> getSerializer() {
        return ModRecipeTypes.FIRE_ITEM_SERIALIZER.get();
    }

    //序列化器实现类
    public static class Serializer extends ForgeRegistryEntry<RecipeSerializer<?>> implements RecipeSerializer<FireItemRecipe> {
        public static final String NAME = ModConstant.MOD_ID + ":fire_item_crafting";

        //从json转为配方
        @Override
        public @NotNull FireItemRecipe fromJson(@NotNull ResourceLocation id, @NotNull JsonObject json) {
            //获取配方物品成分
            NonNullList<Ingredient> nonnulllist = ModJsonReader.ingredientsFromJson(GsonHelper.getAsJsonArray(json, "ingredients"));
            //判断配方成分的可用性
            if (nonnulllist.isEmpty()) {
                throw new JsonParseException("火焰燃烧(物品)配方内没有任何原料 No ingredients for fire item recipe");
            } else if (nonnulllist.size() > Config.WORLD_MAX_COUNT.get()) {
                throw new JsonParseException("火焰燃烧(物品)配方内的原料超出上限" + Config.WORLD_MAX_COUNT.get() + "  Too many ingredients for fire item recipe. The maximum is " + Config.WORLD_MAX_COUNT.get());
            }
            //获取配方物品产物
            ImmutableList<ItemStack> output = ImmutableList.copyOf(ModJsonReader.itemsFromJson(json, "output"));
            //判断产物可用性
            if (output.isEmpty()) {
                throw new JsonParseException("火焰燃烧(物品)配方内没有任何产物 No output for fire item recipe");
            } else if (output.size() > 9) {
                throw new JsonParseException("火焰燃烧(物品)配方内产物超过9 Output type more then 9 for fire item recipe");
            }
            //获取可用火焰
            ImmutableList<Block> fire = ImmutableList.copyOf(ModJsonReader.blocksFromJson(json, "fire"));
            //判断火焰可用性
            if (fire.isEmpty()) {
                throw new JsonParseException("火焰燃烧(物品)配方内没有指定火焰 No fire block for fire item recipe");
            }
            //返回配方
            return new FireItemRecipe(id, fire, output, nonnulllist);
        }

        //通过网络获取配方
        @Nullable
        @Override
        public FireItemRecipe fromNetwork(@NotNull ResourceLocation id, FriendlyByteBuf buffer) {
            //创建方块集合构造器
            ImmutableList.Builder<Block> builderB = ImmutableList.builder();
            //创建物品集合构造器
            ImmutableList.Builder<ItemStack> builderI = ImmutableList.builder();
            //获取火焰数量
            int fireCount = buffer.readVarInt();
            //从buffer中取出指定数量的火焰方块并放入集合中
            for (int i = 0; i < fireCount; i++) {
                builderB.add(ForgeRegistries.BLOCKS.getValue(buffer.readResourceLocation()));
            }
            //构建集合
            ImmutableList<Block> fire = builderB.build();
            //从buffer中获取产物数量
            int outputCount = buffer.readVarInt();
            //从buffer中获取指定数量的产物
            for (int i = 0; i < outputCount; i++) {
                builderI.add(buffer.readItem());
            }
            //构建集合
            ImmutableList<ItemStack> output = builderI.build();
            //从buffer中获取配方成分数量并创建集合
            NonNullList<Ingredient> input = NonNullList.withSize(buffer.readVarInt(), Ingredient.EMPTY);
            //从buffer中获取指定数量成分
            for (int i = 0; i < input.size(); i++) {
                input.set(i, Ingredient.fromNetwork(buffer));
            }
            //返回配方
            return new FireItemRecipe(id, fire, output, input);
        }

        //将配方发送到网络中
        @Override
        public void toNetwork(FriendlyByteBuf buffer, FireItemRecipe recipe) {
            //获取火焰数量
            int fireCount = recipe.fireBlock.size();
            //向buffer中写入火焰数量
            buffer.writeVarInt(fireCount);
            //遍历火焰并将火焰注册名存入buffer
            for (Block block : recipe.fireBlock) {
                buffer.writeResourceLocation(block.getRegistryName());
            }
            //向buffer中写入产物数量
            buffer.writeVarInt(recipe.outputs.size());
            //遍历产物并将物品写入buffer
            for (ItemStack output : recipe.outputs) {
                buffer.writeItemStack(output, false);
            }
            //向buffer中写入成分数量
            buffer.writeVarInt(recipe.inputs.size());
            //遍历成分并将成分写入buffer
            for (Ingredient input : recipe.inputs) {
                input.toNetwork(buffer);
            }
        }
    }
}
